
#pragma once

#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "Logger.h"

namespace xncc {
enum class PARAM_TYPE : std::int8_t
{
    INVALID = -1,
    INTEGER,
    FLOAT,
    STRING,
    PTR,
    UNSIGNED_INTEGER,
    HEX,
    CHAR,
    FUNCTION,
    CONST_STRING,
    PERCENT_SIGN,
    FORMAT_STR_END
};

struct ParamStaticInfo
{
    const char* fmtStr;
    std::size_t posOrLen;
    PARAM_TYPE  type;
};
#if __cplusplus >= 201402L

template <typename T>
struct remove_cv_ref_t
{
    using type = std::remove_cv_t<std::remove_reference_t<T>>;
};

static constexpr int32_t UNKNOWNED_FMT_ID = -1;
template <std::size_t N>
constexpr auto toParamStaticInfo(const char (&fmt)[N], std::size_t pos) -> ParamStaticInfo
{
    switch (fmt[pos]) {
        case 'd': return {fmt, pos, PARAM_TYPE::INTEGER};
        case 'f': return {fmt, pos, PARAM_TYPE::FLOAT};
        case 'F': return {fmt, pos, PARAM_TYPE::FUNCTION};
        case 's': return {fmt, pos, PARAM_TYPE::STRING};
        case 'S': return {fmt, pos, PARAM_TYPE::CONST_STRING};
        case 'p': return {fmt, pos, PARAM_TYPE::PTR};
        case 'u': return {fmt, pos, PARAM_TYPE::UNSIGNED_INTEGER};
        case 'c': return {fmt, pos, PARAM_TYPE::CHAR};
        case 'x': return {fmt, pos, PARAM_TYPE::HEX};
        case '%': return {fmt, pos, PARAM_TYPE::PERCENT_SIGN};
        default: {
            throw std::invalid_argument("only support %c %d %f %s %p %u %F %S");
        }
    }
}
template <std::size_t N>
constexpr auto getParamInfo(const char (&fmt)[N], std::size_t paramCount = 0) -> ParamStaticInfo
{
    std::size_t pos = 0;
    while (pos < N - 1) {
        if (fmt[pos] != '%') {
            ++pos;
            continue;
        }
        ++pos;
        if (fmt[pos] == '%') {
            ++pos;
            continue;
        }
        if (paramCount == 0) {
            return toParamStaticInfo(fmt, pos);
        }
        --paramCount;
        ++pos;
    }
    return {fmt, N - 1, PARAM_TYPE::FORMAT_STR_END};
}

template <std::size_t N>
constexpr auto getParamInfoPercentSign(const char (&fmt)[N], std::size_t paramCount = 0) -> ParamStaticInfo
{
    std::size_t pos = 0;
    while (pos < N - 1) {
        if (fmt[pos] != '%') {
            ++pos;
            continue;
        }
        ++pos;
        if (paramCount == 0) {
            return toParamStaticInfo(fmt, pos);
        }
        --paramCount;
        ++pos;
    }
    return {fmt, N - 1, PARAM_TYPE::FORMAT_STR_END};
}

template <std::size_t N>
constexpr auto getParamInfoDetail(const std::array<ParamStaticInfo, N>& arr, std::size_t index) -> ParamStaticInfo
{
    if (index == 0) {
        if (arr[index].type == PARAM_TYPE::FORMAT_STR_END) {
            return {arr[0].fmtStr, arr[0].posOrLen, arr[0].type};
        }
        return {arr[0].fmtStr, arr[0].posOrLen - 1, arr[0].type};
    }
    if (arr[index].type == PARAM_TYPE::FORMAT_STR_END) {  // pos是% 的位置
        return {arr[index].fmtStr + arr[index - 1].posOrLen + 1, arr[index].posOrLen - arr[index - 1].posOrLen - 1,
                arr[index].type};
    }
    return {arr[index].fmtStr + arr[index - 1].posOrLen + 1, arr[index].posOrLen - arr[index - 1].posOrLen - 2,
            arr[index].type};  // -2 是%d 的长度两个字符 其实char*位置 是上一个的pos
}

template <std::size_t N>
constexpr auto getFormatParamsCount(const char (&fmt)[N]) -> std::size_t
{
    std::size_t count = 0;
    while (getParamInfo(fmt, count).type != PARAM_TYPE::FORMAT_STR_END) {
        ++count;
    }
    ++count;
    return count;
}

template <std::size_t N>
constexpr auto getFormatParamsCountPercentSign(const char (&fmt)[N]) -> std::size_t
{
    std::size_t count = 0;
    while (getParamInfoPercentSign(fmt, count).type != PARAM_TYPE::FORMAT_STR_END) {
        ++count;
    }
    ++count;
    return count;
}

template <std::size_t N, std::size_t... Indices>
constexpr auto parseFormat(const std::array<ParamStaticInfo, N>& arr, std::index_sequence<Indices...>)
    -> std::array<ParamStaticInfo, sizeof...(Indices)>
{
    return {{getParamInfoDetail(arr, Indices)...}};
}

template <std::size_t N, std::size_t... Indices>
constexpr auto parseFormatPercentSign(const char (&fmt)[N], std::index_sequence<Indices...>)
    -> std::array<ParamStaticInfo, sizeof...(Indices)>
{
    return {{getParamInfoPercentSign(fmt, Indices)...}};
}

template <std::size_t N, std::size_t... Indices>
constexpr auto parseFormat(const char (&fmt)[N], std::index_sequence<Indices...>)
    -> std::array<ParamStaticInfo, sizeof...(Indices)>
{
    return {{getParamInfo(fmt, Indices)...}};
}

template <std::size_t paramCount, size_t N>
constexpr auto parseFormatStr(const char (&fmt)[N]) -> std::array<ParamStaticInfo, paramCount>
{
    return parseFormat(parseFormat(fmt, std::make_index_sequence<paramCount>{}),
                       std::make_index_sequence<paramCount>{});
}

template <std::size_t paramCount, size_t N>
constexpr auto parseFormatStrPercentSign(const char (&fmt)[N]) -> std::array<ParamStaticInfo, paramCount>
{
    return parseFormat(parseFormatPercentSign(fmt, std::make_index_sequence<paramCount>{}),
                       std::make_index_sequence<paramCount>{});
}

constexpr auto getArgSize(const PARAM_TYPE paramType, const char* str) -> std::size_t
{
    if (paramType != PARAM_TYPE::STRING) {
        throw std::invalid_argument("type error expected %s");
    }
    return sizeof(int32_t) + strlen(str);
}

constexpr auto getArgSize(const PARAM_TYPE paramType, const void* /*str*/) -> std::size_t
{
    if (paramType != PARAM_TYPE::PTR) {
        throw std::invalid_argument("type error expected %p");
    }
    return sizeof(void*);
}

constexpr auto getArgSize(const PARAM_TYPE paramType, StaticString /*str*/) -> std::size_t
{
    if (paramType != PARAM_TYPE::CONST_STRING) {
        throw std::invalid_argument("type error expected %S");
    }
    return sizeof(void*);
}

template <class T, typename Func>
constexpr auto getArgSize(const PARAM_TYPE paramType, const FunctionObject<T, Func>& arg) -> std::size_t
{
    if (paramType != PARAM_TYPE::FUNCTION) {
        throw std::invalid_argument("type error expected %F");
    }
    return sizeof(arg) + sizeof(T);
}

template <typename T>
constexpr auto getArgSize(const PARAM_TYPE paramType, T /*arg*/)
    -> std::enable_if_t<std::is_same<typename remove_cv_ref_t<T>::type, short>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, int>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, bool>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, long>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, long long>::value,
                        std::size_t>
{
    if (paramType == PARAM_TYPE::INTEGER || paramType == PARAM_TYPE::HEX) {
        return sizeof(long long);
    }
    throw std::invalid_argument("type error expected %d or %x");
}

template <typename T>
constexpr auto getArgSize(const PARAM_TYPE paramType, T /*arg*/)
    -> std::enable_if_t<std::is_same<typename remove_cv_ref_t<T>::type, unsigned short>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, unsigned int>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, unsigned long>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, unsigned long long>::value,
                        std::size_t>
{
    if (paramType == PARAM_TYPE::UNSIGNED_INTEGER || paramType == PARAM_TYPE::HEX) {
        return sizeof(unsigned long long);
    }
    throw std::invalid_argument("type error expected %u or %x");
}

template <typename T>
constexpr auto getArgSize(const PARAM_TYPE paramType, T /*arg*/)
    -> std::enable_if_t<std::is_same<typename remove_cv_ref_t<T>::type, float>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, double>::value,
                        std::size_t>
{
    if (paramType != PARAM_TYPE::FLOAT) {
        throw std::invalid_argument("type error expected %f");
    }
    return sizeof(double);
}

template <typename T>
constexpr auto getArgSize(const PARAM_TYPE paramType, T /*arg*/)
    -> std::enable_if_t<std::is_same<typename remove_cv_ref_t<T>::type, char>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, unsigned char>::value,
                        std::size_t>
{
    if (paramType != PARAM_TYPE::CHAR) {
        throw std::invalid_argument("type error expected %c");
    }
    return sizeof(char);
}

template <std::size_t argIndex = 0, std::size_t paramCount>
constexpr auto getArgsSize(const std::array<ParamStaticInfo, paramCount>& /*paramsInfo*/) -> std::size_t
{
    return 0;
}

// add constexpr 能在编译期算 就编译期算
template <std::size_t argIndex = 0, std::size_t paramCount, typename T1, typename... Ts>
constexpr auto getArgsSize(const std::array<ParamStaticInfo, paramCount>& paramsInfo, T1 head, Ts... rest)
    -> std::size_t
{
    return getArgSize(paramsInfo[argIndex].type, head) + getArgsSize<argIndex + 1, paramCount>(paramsInfo, rest...);
}

inline void storeArgData(char* buffer, uint64_t& offset, const char* str)
{
    const auto str_len                            = static_cast<uint32_t>(__builtin_strlen(str));
    *reinterpret_cast<uint32_t*>(buffer + offset) = str_len;
    offset += sizeof(uint32_t);
    memcpy(buffer + offset, str, str_len);
    offset += str_len;
}

inline void storeArgData(char* buffer, uint64_t& offset, StaticString str)
{
    *reinterpret_cast<uintptr_t*>(buffer + offset) = reinterpret_cast<uintptr_t>(str.data);
    offset += sizeof(uintptr_t);
}

template <class T, typename Func>
inline void storeArgData(char* buffer, uint64_t& offset, const FunctionObject<T, Func>& arg)
{
    memcpy(buffer + offset, &arg, sizeof(arg));
    offset += sizeof(arg);
    memcpy(buffer + offset, arg.innerType.dataPtr, sizeof(T));
    offset += sizeof(T);
}

inline void storeArgData(char* buffer, uint64_t& offset, const void* str)
{
    *reinterpret_cast<uintptr_t*>(buffer + offset) = reinterpret_cast<uintptr_t>(str);
    offset += sizeof(uintptr_t);
}

template <typename T>
inline auto storeArgData(char* buffer, uint64_t& offset, T arg)
    -> std::enable_if_t<std::is_same<typename remove_cv_ref_t<T>::type, short>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, int>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, bool>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, long>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, long long>::value,
                        void>
{
    *reinterpret_cast<long long*>(buffer + offset) = arg;
    offset += sizeof(long long);
}

template <typename T>
inline auto storeArgData(char* buffer, uint64_t& offset, T arg)
    -> std::enable_if_t<std::is_same<typename remove_cv_ref_t<T>::type, unsigned short>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, unsigned int>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, unsigned long>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, unsigned long long>::value,
                        void>
{
    *reinterpret_cast<unsigned long long*>(buffer + offset) = arg;
    offset += sizeof(unsigned long long);
}

template <typename T>
inline auto storeArgData(char* buffer, uint64_t& offset, T arg)
    -> std::enable_if_t<std::is_same<typename remove_cv_ref_t<T>::type, float>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, double>::value,
                        void>
{
    *reinterpret_cast<double*>(buffer + offset) = arg;
    offset += sizeof(double);
}

template <typename T>
inline auto storeArgData(char* buffer, uint64_t& offset, T arg)
    -> std::enable_if_t<std::is_same<typename remove_cv_ref_t<T>::type, char>::value ||
                            std::is_same<typename remove_cv_ref_t<T>::type, unsigned char>::value,
                        void>
{
    *(buffer + offset) = arg;
    offset += sizeof(char);
}

inline void storeArgDatas(char* buffer, uint64_t& offset)
{
}

template <typename T1, typename... Ts>
inline void storeArgDatas(char* buffer, uint64_t& offset, T1 head, Ts... rest)
{
    storeArgData(buffer, offset, head);
    storeArgDatas(buffer, offset, rest...);
}

template <typename... Args>
struct LogbinaryType : std::false_type
{
};

template <>
struct LogbinaryType<> : std::false_type
{
};

template <typename Head, typename... Tail>
struct LogbinaryType<Head, Tail...>
    : std::conditional_t<std::is_standard_layout<Head>::value && std::is_trivially_copyable<Head>::value,
                         std::true_type,
                         LogbinaryType<Tail...>>
{
};
template <typename T>
struct LogbinaryType<T>
    : std::integral_constant<bool, std::is_standard_layout<T>::value && std::is_trivially_copyable<T>::value>
{
};

class LogNewObject
{
  public:
    LogNewObject(std::size_t            argsSize,
                 int32_t&               fmtId,
                 const ParamStaticInfo* params,
                 xncc::LOG_LEVEL        level,
                 const char*            file,
                 uint32_t               line,
                 const char*            function,
                 uint16_t               loggerId);

    LogNewObject(std::size_t argsSize, int32_t& fmtId, const ParamStaticInfo* params, uint16_t loggerId);
    ~LogNewObject();

    LogNewObject(const LogNewObject&)                    = delete;
    auto operator=(const LogNewObject&) -> LogNewObject& = delete;
    LogNewObject(LogNewObject&&)                         = delete;
    auto operator=(LogNewObject&&) -> LogNewObject&      = delete;

    auto getBuffer() -> char*
    {
        return logBuf_;
    }

    auto getOffset() const -> uint64_t
    {
        return offset_;
    }

    void setOffset(const uint64_t offset)
    {
        offset_ = offset;
    }

  private:
    char*    logBuf_;
    uint64_t offset_;
    bool     over_flag = true;
};

class LogNewBinary
{
  public:
    LogNewBinary(std::size_t argsSize, uint16_t loggerId);

    ~LogNewBinary();
    LogNewBinary(const LogNewBinary&)                    = delete;
    auto operator=(const LogNewBinary&) -> LogNewBinary& = delete;
    LogNewBinary(LogNewBinary&&)                         = delete;
    auto operator=(LogNewBinary&&) -> LogNewBinary&      = delete;

    auto getBuffer() -> char*
    {
        return logBuf_;
    }

    auto getOffset() const -> uint64_t
    {
        return offset_;
    }

    void setOffset(const uint64_t offset)
    {
        offset_ = offset;
    }

  private:
    char*    logBuf_;
    uint64_t offset_;
};

template <std::size_t paramCount, size_t N, typename... Ts>
void xncc_log(xncc::LOG_LEVEL                                level,
              const char*                                    file,
              uint32_t                                       line,
              const char*                                    function,
              uint16_t                                       loggerId,
              int32_t&                                       fmtId,
              const std::array<ParamStaticInfo, paramCount>& paramsInfo,
              const ParamStaticInfo*                         paramsInfoPercentSign,
              const char (&fmt)[N],
              Ts&&... args)
{
    LogNewObject obj(getArgsSize<0, paramCount>(paramsInfo, std::forward<Ts>(args)...), fmtId, paramsInfoPercentSign,
                     level, file, line, function, loggerId);
    char*        logbuf = obj.getBuffer();
    auto         offset = obj.getOffset();
    storeArgDatas(logbuf, offset, std::forward<Ts>(args)...);
    obj.setOffset(offset);
}

template <std::size_t paramCount, size_t N, typename... Ts>
void xncc_log_data(uint16_t                                       loggerId,
                   int32_t&                                       fmtId,
                   const std::array<ParamStaticInfo, paramCount>& paramsInfo,
                   const ParamStaticInfo*                         paramsInfoPercentSign,
                   const char (&fmt)[N],
                   Ts&&... args)
{
    LogNewObject obj(getArgsSize<0, paramCount>(paramsInfo, std::forward<Ts>(args)...), fmtId, paramsInfoPercentSign,
                     loggerId);
    char*        logbuf = obj.getBuffer();
    auto         offset = obj.getOffset();
    storeArgDatas(logbuf, offset, std::forward<Ts>(args)...);
    obj.setOffset(offset);
}

template <typename T>
void appendToBuffer(char* buffer, size_t& offset, T&& value)
{
    std::memcpy((buffer) + offset, &value, sizeof(value));
    offset += sizeof(value);
}

inline void appendArgsToBuffer(char* buffer, size_t& offset)
{
}

// 递归模板，用于处理参数包
template <typename T, typename... Args>
void appendArgsToBuffer(char* buffer, size_t& offset, T&& first, Args&&... rest)
{
    appendToBuffer(buffer, offset, std::forward<T>(first));
    appendArgsToBuffer(buffer, offset, std::forward<Args>(rest)...);
}

// 主函数，用于启动参数复制过程
template <typename... Args>
void copyArgsToBuffer(char* buffer, Args&&... args)
{
    size_t offset = 0;
    appendArgsToBuffer(buffer, offset, std::forward<Args>(args)...);
}

template <typename T>
constexpr auto calcArgSize(T&& value) -> std::size_t
{
    static_assert(std::is_standard_layout<typename remove_cv_ref_t<T>::type>::value, "must be standard_layout type");
    static_assert(std::is_trivially_copyable<typename remove_cv_ref_t<T>::type>::value,
                  "must be trivially_copyable type");
    return sizeof(value);
}

constexpr auto calcArgsSize() -> std::size_t
{
    return 0;
}

// 递归模板，用于处理参数包
template <typename T, typename... Args>
constexpr auto calcArgsSize(T&& first, Args&&... rest) -> std::size_t
{
    return calcArgSize(std::forward<T>(first)) + calcArgsSize(std::forward<Args>(rest)...);
}

template <std::size_t argSize, typename... Ts>
void xncc_log_bin(uint16_t loggerId, Ts&&... args)
{
    LogNewBinary obj(argSize, loggerId);
    char*        logbuf = obj.getBuffer();
    auto         offset = obj.getOffset();
    copyArgsToBuffer(logbuf + offset, std::forward<Ts>(args)...);
}
}  // namespace xncc

template <typename... Args>
auto narg(Args&&... args) -> char (&)[sizeof...(Args) + 1];

#endif
// only log data
#define XNCC_LOG(logger, format, ...)                                                                                  \
    do {                                                                                                               \
        auto constexpr paramCount = xncc::getFormatParamsCount(format);                                                \
        static_assert(paramCount == sizeof(::narg(__VA_ARGS__)), "params count error");                                \
        static auto constexpr paramsInfo = xncc::parseFormatStr<paramCount>(format);                                   \
        static int32_t fmtId             = xncc::UNKNOWNED_FMT_ID;                                                     \
        static auto constexpr paramsInfoPercentSign =                                                                  \
            xncc::parseFormatStrPercentSign<xncc::getFormatParamsCountPercentSign(format)>(format);                    \
        xncc::xncc_log_data<paramCount>(logger.loggerId(), fmtId, paramsInfo, &paramsInfoPercentSign[0], format,       \
                                        ##__VA_ARGS__);                                                                \
    } while (0)

#define XNCC_LOG_BIN(logger, ...)                                                                                      \
    do {                                                                                                               \
        static_assert((sizeof(::narg(__VA_ARGS__)) > 1), "params count error");                                        \
        constexpr auto argSize = xncc::calcArgsSize(__VA_ARGS__);                                                      \
        xncc::xncc_log_bin<argSize>(logger.loggerId(), ##__VA_ARGS__);                                                 \
    } while (0)

#define XNCC_LOG_DEBUG(logger, format, ...)                                                                            \
    do {                                                                                                               \
        if (logger.getLevel() >= xncc::LOG_LEVEL::DEBUG) {                                                             \
            auto constexpr paramCount = xncc::getFormatParamsCount(format);                                            \
            static_assert(paramCount == sizeof(::narg(__VA_ARGS__)), "params count error");                            \
            static auto constexpr paramsInfo = xncc::parseFormatStr<paramCount>(format);                               \
            static int32_t fmtId             = xncc::UNKNOWNED_FMT_ID;                                                 \
            static auto constexpr paramsInfoPercentSign =                                                              \
                xncc::parseFormatStrPercentSign<xncc::getFormatParamsCountPercentSign(format)>(format);                \
            xncc::xncc_log<paramCount>(xncc::LOG_LEVEL::DEBUG, __FILE__, __LINE__, __FUNCTION__, logger.loggerId(),    \
                                       fmtId, paramsInfo, &paramsInfoPercentSign[0], format, ##__VA_ARGS__);           \
        }                                                                                                              \
    } while (0)

#define XNCC_LOG_INFO(logger, format, ...)                                                                             \
    do {                                                                                                               \
        if (logger.getLevel() >= xncc::LOG_LEVEL::INFO) {                                                              \
            auto constexpr paramCount = xncc::getFormatParamsCount(format);                                            \
            static_assert(paramCount == sizeof(::narg(__VA_ARGS__)), "params count error");                            \
            static auto constexpr paramsInfo = xncc::parseFormatStr<paramCount>(format);                               \
            static int32_t fmtId             = xncc::UNKNOWNED_FMT_ID;                                                 \
            static auto constexpr paramsInfoPercentSign =                                                              \
                xncc::parseFormatStrPercentSign<xncc::getFormatParamsCountPercentSign(format)>(format);                \
            xncc::xncc_log<paramCount>(xncc::LOG_LEVEL::INFO, __FILE__, __LINE__, __FUNCTION__, logger.loggerId(),     \
                                       fmtId, paramsInfo, &paramsInfoPercentSign[0], format, ##__VA_ARGS__);           \
        }                                                                                                              \
    } while (0)

#define XNCC_LOG_WARN(logger, format, ...)                                                                             \
    do {                                                                                                               \
        if (logger.getLevel() >= xncc::LOG_LEVEL::WARN) {                                                              \
            auto constexpr paramCount = xncc::getFormatParamsCount(format);                                            \
            static_assert(paramCount == sizeof(::narg(__VA_ARGS__)), "params count error");                            \
            static auto constexpr paramsInfo = xncc::parseFormatStr<paramCount>(format);                               \
            static int32_t fmtId             = xncc::UNKNOWNED_FMT_ID;                                                 \
            static auto constexpr paramsInfoPercentSign =                                                              \
                xncc::parseFormatStrPercentSign<xncc::getFormatParamsCountPercentSign(format)>(format);                \
            xncc::xncc_log<paramCount>(xncc::LOG_LEVEL::WARN, __FILE__, __LINE__, __FUNCTION__, logger.loggerId(),     \
                                       fmtId, paramsInfo, &paramsInfoPercentSign[0], format, ##__VA_ARGS__);           \
        }                                                                                                              \
    } while (0)

#define XNCC_LOG_ERROR(logger, format, ...)                                                                            \
    do {                                                                                                               \
        auto constexpr paramCount = xncc::getFormatParamsCount(format);                                                \
        static_assert(paramCount == sizeof(::narg(__VA_ARGS__)), "params count error");                                \
        static auto constexpr paramsInfo = xncc::parseFormatStr<paramCount>(format);                                   \
        static int32_t fmtId             = xncc::UNKNOWNED_FMT_ID;                                                     \
        static auto constexpr paramsInfoPercentSign =                                                                  \
            xncc::parseFormatStrPercentSign<xncc::getFormatParamsCountPercentSign(format)>(format);                    \
        xncc::xncc_log<paramCount>(xncc::LOG_LEVEL::ERROR, __FILE__, __LINE__, __FUNCTION__, logger.loggerId(), fmtId, \
                                   paramsInfo, &paramsInfoPercentSign[0], format, ##__VA_ARGS__);                      \
    } while (0)

#define XNCC_LOG_FATAL(logger, format, ...)                                                                            \
    do {                                                                                                               \
        auto constexpr paramCount = xncc::getFormatParamsCount(format);                                                \
        static_assert(paramCount == sizeof(::narg(__VA_ARGS__)), "params count error");                                \
        static auto constexpr paramsInfo = xncc::parseFormatStr<paramCount>(format);                                   \
        static int32_t fmtId             = xncc::UNKNOWNED_FMT_ID;                                                     \
        static auto constexpr paramsInfoPercentSign =                                                                  \
            xncc::parseFormatStrPercentSign<xncc::getFormatParamsCountPercentSign(format)>(format);                    \
        xncc::xncc_log<paramCount>(xncc::LOG_LEVEL::FATAL, __FILE__, __LINE__, __FUNCTION__, logger.loggerId(), fmtId, \
                                   paramsInfo, &paramsInfoPercentSign[0], format, ##__VA_ARGS__);                      \
    } while (0)
